﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using IDE;

namespace wpf_nes
{
    public class Plugin : IDE.IPlugin
    {
        public string Name => "NES Emulator";

        public static IDE.I_IDE _IDE
        {
            get;
            private set;
        }

        public IDE.I_IDE IDE
        {
            get
            {
                return _IDE;
            }
            set
            {
                if (_IDE == null)
                    _IDE = value;
            }
        }
        public PanelInfo[] panels => new PanelInfo[]
        {
            new PanelInfo(){ type="view", title="nes:view", description="", defaultWidth=500, defaultHeight=400},
            new PanelInfo(){type="cpu",title="nes:cpu",description="",defaultWidth=500,defaultHeight=400}

        }
        ;

        public FileTypeDesc[] filetypes => null;

        public ToolBar[] toolbars => null;

        public MenuOption[] MenuItem => null;

        public void CreateEmptyFile(string type, string filename)
        {
            throw new NotImplementedException();
        }

        public UIElement CreatePanelUI(string type)
        {
            if (type == "view")
                return new Control_Nes_View();
            else if (type == "cpu")
                return new Control_Nes_CPU();
            throw new NotImplementedException();
        }

        public void OnMenuClick(string path)
        {
            throw new NotImplementedException();
        }
        public IDocument OpenFile(string extname, string filename, bool writeable)
        {
            throw new NotImplementedException();
        }
    }
    public interface IWantKnownUpdate
    {
        void Update();
    }
    public static class staticfunc
    {
        public static Nescafe.Console console;
        public static WriteableBitmap frameWpf;
        static UInt64 applyFrame = 0;
        static Thread consoleThread;
        static DispatcherTimer dispatcherTimer = new DispatcherTimer();
        static int[] btnstates;
        static staticfunc()
        {
            btnstates = new int[8];

            console = new Nescafe.Console();
            //console.DrawAction = ConsoleThread_OnDraw;

            frameWpf = new WriteableBitmap(256, 240, 96, 96, PixelFormats.Indexed8, InitPaletteWpf());

            dispatcherTimer = new DispatcherTimer();
            dispatcherTimer.Tick += new EventHandler(CurThread_UpdateBitmap);
            dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 10);

            //把这个动作绑起来
            Plugin._IDE.GetPipeline("_project").Care(
                (tag, arg) =>
                {
                    if (tag == "run")
                    {
                        Start();
                    }
                    if (tag == "stop")
                    {
                        Stop();
                    }
                },
                null); ;
        }
        public static void Start()
        {
            Stop();


            dispatcherTimer.Start();

            console.Clear();
            var curimage = Plugin._IDE.GetData_ByteArray("__curImage");
            if (curimage != null)
            {
                console.LoadCartridgeFromBytes(curimage);

                consoleThread = new Thread(() => console.Start());
                consoleThread.IsBackground = true;
                consoleThread.Start();
            }
            else
            {
                System.Windows.MessageBox.Show("no image had be build. check it.");
                //console.LoadCartridge("d:\\Super_mario_brothers2.nes");
            }
            //console.Start();

        }

        public static void Stop()
        {
            dispatcherTimer.Stop();
            if (consoleThread != null)
            {
                consoleThread.Abort();
            }
        }
        static BitmapPalette InitPaletteWpf()
        {
            Color[] colors = new Color[64];
            colors[0x0] = Color.FromRgb(84, 84, 84);
            colors[0x1] = Color.FromRgb(0, 30, 116);
            colors[0x2] = Color.FromRgb(8, 16, 144);
            colors[0x3] = Color.FromRgb(48, 0, 136);
            colors[0x4] = Color.FromRgb(68, 0, 100);
            colors[0x5] = Color.FromRgb(92, 0, 48);
            colors[0x6] = Color.FromRgb(84, 4, 0);
            colors[0x7] = Color.FromRgb(60, 24, 0);
            colors[0x8] = Color.FromRgb(32, 42, 0);
            colors[0x9] = Color.FromRgb(8, 58, 0);
            colors[0xa] = Color.FromRgb(0, 64, 0);
            colors[0xb] = Color.FromRgb(0, 60, 0);
            colors[0xc] = Color.FromRgb(0, 50, 60);
            colors[0xd] = Color.FromRgb(0, 0, 0);
            colors[0xe] = Color.FromRgb(0, 0, 0);
            colors[0xf] = Color.FromRgb(0, 0, 0);
            colors[0x10] = Color.FromRgb(152, 150, 152);
            colors[0x11] = Color.FromRgb(8, 76, 196);
            colors[0x12] = Color.FromRgb(48, 50, 236);
            colors[0x13] = Color.FromRgb(92, 30, 228);
            colors[0x14] = Color.FromRgb(136, 20, 176);
            colors[0x15] = Color.FromRgb(160, 20, 100);
            colors[0x16] = Color.FromRgb(152, 34, 32);
            colors[0x17] = Color.FromRgb(120, 60, 0);
            colors[0x18] = Color.FromRgb(84, 90, 0);
            colors[0x19] = Color.FromRgb(40, 114, 0);
            colors[0x1a] = Color.FromRgb(8, 124, 0);
            colors[0x1b] = Color.FromRgb(0, 118, 40);
            colors[0x1c] = Color.FromRgb(0, 102, 120);
            colors[0x1d] = Color.FromRgb(0, 0, 0);
            colors[0x1e] = Color.FromRgb(0, 0, 0);
            colors[0x1f] = Color.FromRgb(0, 0, 0);
            colors[0x20] = Color.FromRgb(236, 238, 236);
            colors[0x21] = Color.FromRgb(76, 154, 236);
            colors[0x22] = Color.FromRgb(120, 124, 236);
            colors[0x23] = Color.FromRgb(176, 98, 236);
            colors[0x24] = Color.FromRgb(228, 84, 236);
            colors[0x25] = Color.FromRgb(236, 88, 180);
            colors[0x26] = Color.FromRgb(236, 106, 100);
            colors[0x27] = Color.FromRgb(212, 136, 32);
            colors[0x28] = Color.FromRgb(160, 170, 0);
            colors[0x29] = Color.FromRgb(116, 196, 0);
            colors[0x2a] = Color.FromRgb(76, 208, 32);
            colors[0x2b] = Color.FromRgb(56, 204, 108);
            colors[0x2c] = Color.FromRgb(56, 180, 204);
            colors[0x2d] = Color.FromRgb(60, 60, 60);
            colors[0x2e] = Color.FromRgb(0, 0, 0);
            colors[0x2f] = Color.FromRgb(0, 0, 0);
            colors[0x30] = Color.FromRgb(236, 238, 236);
            colors[0x31] = Color.FromRgb(168, 204, 236);
            colors[0x32] = Color.FromRgb(188, 188, 236);
            colors[0x33] = Color.FromRgb(212, 178, 236);
            colors[0x34] = Color.FromRgb(236, 174, 236);
            colors[0x35] = Color.FromRgb(236, 174, 212);
            colors[0x36] = Color.FromRgb(236, 180, 176);
            colors[0x37] = Color.FromRgb(228, 196, 144);
            colors[0x38] = Color.FromRgb(204, 210, 120);
            colors[0x39] = Color.FromRgb(180, 222, 120);
            colors[0x3a] = Color.FromRgb(168, 226, 144);
            colors[0x3b] = Color.FromRgb(152, 226, 180);
            colors[0x3c] = Color.FromRgb(160, 214, 228);
            colors[0x3d] = Color.FromRgb(160, 162, 160);
            colors[0x3e] = Color.FromRgb(0, 0, 0);
            colors[0x3f] = Color.FromRgb(0, 0, 0);
            return new BitmapPalette(colors);
        }

        //public unsafe static void ConsoleThread_OnDraw(byte[] screen)
        //{
        //    FrameId++;
        //}
        public static void PressButton(Nescafe.Controller.Button btn)
        {
            btnstates[(int)btn] = 5;
            console.Controller.setButtonState(btn, true);
        }
        public static void WantKnownUpdate(IWantKnownUpdate update)
        {
            wantlist.Add(new System.WeakReference<IWantKnownUpdate>(update));
        }
        static unsafe void CurThread_UpdateBitmap(object send, EventArgs arg)
        {
            if (console.Ppu.FrameID != applyFrame)
            {
                for (var i = 0; i < 8; i++)
                {
                    if (btnstates[i] > 0)
                    {
                        btnstates[i]--;
                        if (btnstates[i] == 0)
                        {
                            console.Controller.setButtonState((Nescafe.Controller.Button)i, false);
                        }
                    }
                }
                staticfunc.frameWpf.Lock();
                fixed (void* bs = console.Ppu.BitmapData)
                {
                    IntPtr buffer = new IntPtr(bs);
                    staticfunc.frameWpf.WritePixels(new System.Windows.Int32Rect(0, 0, 256, 240), buffer, 256 * 240, 256);
                }
                staticfunc.frameWpf.Unlock();
                applyFrame = staticfunc.console.Ppu.FrameID;
                //update controls who need
                foreach (var i in wantlist)
                {
                    if (i.TryGetTarget(out IWantKnownUpdate target))
                    {
                        target.Update();
                    }
                    else
                    {
                        wantlist.Remove(i);
                        break;
                    }
                }
            }
        }
        static List<System.WeakReference<IWantKnownUpdate>> wantlist = new List<WeakReference<IWantKnownUpdate>>();
    }
}
